package com.pig4cloud.pig.village.rules.generator;

import java.util.ArrayList;
import java.util.List;

import com.pig4cloud.pig.village.api.entity.WelfareRule;
import com.pig4cloud.pig.village.rules.RuleCondition;
import com.pig4cloud.pig.village.rules.generator.conditions.ArrayParser;
import com.pig4cloud.pig.village.rules.generator.conditions.BooleanParser;
import com.pig4cloud.pig.village.rules.generator.conditions.NumericParser;
import com.pig4cloud.pig.village.rules.generator.conditions.NumericRangeParser;
import com.pig4cloud.pig.village.rules.generator.conditions.StringParser;
import org.apache.commons.lang3.StringUtils;

public class RulesGenerator {
	private final static String RULE_KEYWORD = "rule";
	private final static String WHEN_KEYWORD = "when";
	private final static String THEN_KEYWORD = "then";
	private final static String END_KEYWORD = "end";
	private final static String NEWLINE = System.getProperty("line.separator");
	private final static String INDENTATION = "  ";
	private final static String SPACE = " ";
	private final static String QUOTATION = "\"";

	public String generateRule(WelfareRule welfareRule, String ruleName) {
		String factClass = "";
		StringBuilder rule = new StringBuilder();
		StringBuilder when;
		StringBuilder then;

		if (welfareRule.getGroupDimension().equals(0)) {
			factClass = "VillagerData";
		} else if (welfareRule.getGroupDimension().equals(1)) {
			factClass = "FamilyData";
		} else if (welfareRule.getGroupDimension().equals(2)) {
			factClass = "TeamData";
		}

		// TODO Do not limit only 2 conditions
		List<RuleCondition> ruleConditionList = new ArrayList<>();

		if (!StringUtils.isEmpty(welfareRule.getConditionDimension1())) {
			ruleConditionList.add(new RuleCondition(
					welfareRule.getConditionDimension1(),
					welfareRule.getExtentDimension1())
			);
		}

		if (!StringUtils.isEmpty(welfareRule.getConditionDimension2())) {
			ruleConditionList.add(new RuleCondition(
					welfareRule.getConditionDimension2(),
					welfareRule.getExtentDimension2())
			);
		}

		when = parseWhen(factClass, ruleConditionList);
		then = parseThen(welfareRule);

		return rule.append(RULE_KEYWORD).append(SPACE).append(QUOTATION).append(ruleName).append(QUOTATION).append(NEWLINE)
				.append(WHEN_KEYWORD).append(NEWLINE)
				.append(when).append(NEWLINE)
				.append(THEN_KEYWORD).append(NEWLINE)
				.append(then).append(NEWLINE)
				.append(END_KEYWORD).toString();
	}

	private StringBuilder parseWhen(String factClass, List<RuleCondition> ruleConditionList) {
		StringBuilder conditionBuilder = new StringBuilder(INDENTATION)
				.append("$v : ").append(factClass);

		// For "no conditions" scenario
		if (ruleConditionList.isEmpty()) {
			conditionBuilder.append("()").append(SPACE).append("eval(true)");
			return conditionBuilder;
		}

		// Use "AND" for multiple conditions
		conditionBuilder.append("(");
		ruleConditionList.forEach((ruleCondition -> {
			switch (ruleCondition.getValueType()) {
				case ARRAY:
					conditionBuilder.append(new ArrayParser().parse(ruleCondition));
					break;
				case STRING:
					conditionBuilder.append(new StringParser().parse(ruleCondition));
					break;
				case BOOLEAN:
					conditionBuilder.append(new BooleanParser().parse(ruleCondition));
					break;
				case NUMERIC_SINGLE:
					conditionBuilder.append(new NumericParser().parse(ruleCondition));
					break;
				case NUMERIC_RANGE:
					conditionBuilder.append(new NumericRangeParser().parse(ruleCondition));
					break;
			}
			conditionBuilder.append(" && ");
		}));
		conditionBuilder.delete(conditionBuilder.length() - " && ".length(), conditionBuilder.length());
		conditionBuilder.append(")");
		return conditionBuilder;
	}

	private StringBuilder parseThen(WelfareRule welfareRule) {
		String negative = welfareRule.getRaise() == 1 ? "" : "-";
		StringBuilder thenBuilder = new StringBuilder();
		thenBuilder.append(INDENTATION)
				.append("$v.setAmount(\"")
				.append(negative).append(welfareRule.getAmount())
				.append("\");").append(NEWLINE);

		thenBuilder.append(INDENTATION)
				.append("$v.setMatch(true);");
		return thenBuilder;
	}

}
